﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using TSites.Service.Common;
using TSites.Service.Entities;
using TSites.Service.Interfaces;
using TSites.Service.Common.Utils;
using TSites.Offices;

namespace TSites.Service
{
	public class CompanyService : ICompanyService
	{
		#region Fields

		private int GENERATE_CODE = 0;
		private string _path = null;
		private readonly BasedFileProcessor<Company> _xmlCompany = null;
		private Dictionary<string, Company> _companyHash = null;
		
		#endregion

		#region Private methods

		private Dictionary<string, Company> Initialize(string path)
		{
			Dictionary<string, Company> companyHash = new Dictionary<string, Company>(256);
			List<Company> companyList = null;
			try
			{
				companyList = _xmlCompany.Read(path);
				if (companyList != null)
				{
					foreach (Company com in companyList)
					{
						if (!companyHash.ContainsKey(com.ID))
						{
							companyHash.Add(com.ID, com);
						}
					}
				}
			}
			catch (Exception)
			{
				throw;
			}

			GENERATE_CODE = companyHash.Count;
			return companyHash;
		}

		private void Write2File()
		{
			_xmlCompany.Write(_path, _companyHash.Values.ToArray());
		}

		#endregion

		#region Ctor

		public CompanyService(string path = null)
		{
			_xmlCompany = new XmlCompanyProcessor();
			
			if (path == null) // get path from configuration file
			{
				path = GlobalConst.XML_FILE_PATH;
			}
			_path = path;
			this._companyHash = Initialize(path);
		}
		
		#endregion

		public Company GetCompany(string id)
		{
			Company com;
			_companyHash.TryGetValue(id, out com);
			return com;
		}

		public Company[] GetList(string comName, int language)
		{
			Company[] companies = null;
			if (string.IsNullOrEmpty(comName))
			{
				companies = _companyHash.Values.ToArray();
			}
			else
			{
				comName = comName.ToUpperInvariant();
				if (language == 0)
				{
					companies = _companyHash.Values
						.Where(c => c.VietnamesName != null && 
							c.VietnamesName.ToUpperInvariant().Contains(comName)).ToArray();
				}
				else if (language == 1)
				{
					companies = _companyHash.Values
						.Where(c => c.EnglishName != null &&
							c.EnglishName.ToUpperInvariant().Contains(comName)).ToArray();
				}
			}

			return companies;
		}

		public int Insert(Company entity)
		{
			int lengthId = 10;
			entity.ID = RandomString(lengthId);

			// 1. process logo file
			//entity.LogoPath = CopyFile(entity.LogoPath, false);

			// 2. add to hash
			_companyHash.Add(entity.ID, entity);

			// 3. write to file
			Write2File();
			return 1;
		}

		public int Update(Company entity)
		{
			string id = entity.ID;
			Company com;
			if (!_companyHash.TryGetValue(id, out com))
			{
				return 0;
			}

			// 1. process logo file
			//entity.LogoPath = CopyFile(entity.LogoPath, true);

			// 2. update in hash
			entity = com;

			// 3. write to file
			Write2File();
			return 1;
		}

		public int Delete(string id)
		{
			if (!_companyHash.ContainsKey(id))
			{
				return 0;
			}

			// delete in hash
			_companyHash.Remove(id);

			// 3. write to file
			Write2File();
			return 1;
		}

		#region Work with word

		public void ExportWordTemplate(string templatePath, Dictionary<string, object> templateParams,
			string filePath = null, bool isVisible = false, string printerName = null)
		{
			WordObject.ExportAndPrint(templatePath, templateParams, filePath, isVisible, printerName);
		}

		#endregion

		#region Work with excel file

		public List<string> GetWorkSheets(string filePath)
		{
			ExcelObject excel = new ExcelObject(filePath);
			return excel.GetWorkSheets();
		}

		public int ImportExcel(string filePath, string workSheetName)
		{
			int rowAdded = 0;
			List<Company> companies = ReadDataFromExcel(filePath, workSheetName);
			if (companies != null && companies.Count > 0)
			{
				// build company hash whose key is TaxCode to check it was existed already
				Dictionary<string, Company> taxCodeHash = new Dictionary<string, Company>(256);
				// get the old companies
				Add2TaxCodeHash(taxCodeHash, _companyHash.Values.ToList());

				// get the new companies
				Add2TaxCodeHash(taxCodeHash, companies);

				// get the full array of company
				Company[] fullCompanies = taxCodeHash.Values.ToArray();
				foreach (Company com in fullCompanies)
				{
					if (string.IsNullOrWhiteSpace(com.ID)) // the new company
					{
						// generate ID
						com.ID = RandomString(10);
						rowAdded++;
					}
				}

				// write2file
				_xmlCompany.Write(_path, fullCompanies);

				// load to Hash
				_companyHash = Initialize(_path);
			}

			return rowAdded;
		}

		private void Add2TaxCodeHash(Dictionary<string, Company> taxCodeHash, List<Company> companies)
		{
			string taxcode;
			foreach (Company com in companies)
			{
				taxcode = com.TaxCode;
				if (string.IsNullOrWhiteSpace(taxcode))
				{
					// generate tax code
					taxcode = RandomString(10);
					taxCodeHash.Add(taxcode, com);
				}
				else
				{
					if (taxCodeHash.ContainsKey(taxcode)) // existed => update
					{
						Company oldCompany = taxCodeHash[taxcode];
						// get the old ContractNos
						if (oldCompany.ContractNos != null)
						{
							com.ContractNos = com.ContractNos ?? new List<string>();
							foreach (string item in oldCompany.ContractNos)
							{
								if (!com.ContractNos.Contains(item))
								{
									com.ContractNos.Add(item);
								}
							}
						}
						taxCodeHash[taxcode] = com;
					}
					else // not yet => add
					{
						taxCodeHash.Add(taxcode, com);
					}
				}
			}
		}

		private List<Company> ReadDataFromExcel(string filePath, string workSheetName)
		{
			List<Company> companies = null;

			ExcelObject excel = new ExcelObject(filePath);
			DataTable dtData = excel.ReadTable(workSheetName);

			int count;
			if (dtData != null && (count = dtData.Rows.Count) > 0)
			{
				Dictionary<string, bool> columnName = new Dictionary<string, bool>();
				string vnName, enName, contractNo, representedBy, vnposition, enposition, taxCode, phone, fax, vnAddress, enAddress;
				vnName = AppConfig.GetCustomString("ConsistentSection", "VNName");
				enName = AppConfig.GetCustomString("ConsistentSection", "ENName");
				contractNo = AppConfig.GetCustomString("ConsistentSection", "ContractNo");
				representedBy = AppConfig.GetCustomString("ConsistentSection", "RepresentedBy");
				vnposition = AppConfig.GetCustomString("ConsistentSection", "VNPosition");
				enposition = AppConfig.GetCustomString("ConsistentSection", "ENPosition");
				taxCode = AppConfig.GetCustomString("ConsistentSection", "TaxCode");
				phone = AppConfig.GetCustomString("ConsistentSection", "Phone");
				fax = AppConfig.GetCustomString("ConsistentSection", "Fax");
				vnAddress = AppConfig.GetCustomString("ConsistentSection", "VNAddress");
				enAddress = AppConfig.GetCustomString("ConsistentSection", "ENAddress");
				// add to hash
				columnName.Add(vnName, true);
				columnName.Add(enName, true);
				columnName.Add(contractNo, true);
				columnName.Add(representedBy, true);
				columnName.Add(vnposition, true);
				columnName.Add(enposition, true);
				columnName.Add(taxCode, true);
				columnName.Add(phone, true);
				columnName.Add(fax, true);
				columnName.Add(vnAddress, true);
				columnName.Add(enAddress, true);

				// get header index
				int columnHeaderIndex;
				Dictionary<string, int> columnIndex = GetColumnIndex(dtData, columnName, out columnHeaderIndex);

				DataRow dr;
				Company company;
				companies = new List<Company>(count);
				List<string> contractNos;
				for (int i = columnHeaderIndex; i < count; i++)
				{
					dr = dtData.Rows[i];
					contractNos = new List<string>();
					contractNos.Add(ToString(dr[columnIndex[contractNo]]));
					company = new Company
					{
						VietnamesName = ToString(dr[columnIndex[vnName]]),
						EnglishName = ToString(dr[columnIndex[enName]]),
						ContractNos = contractNos,
						RepresentedBy = ToString(dr[columnIndex[representedBy]]),
						VietnamesPosition = ToString(dr[columnIndex[vnposition]]),
						EnglishPosition = ToString(dr[columnIndex[enposition]]),
						TaxCode = ToString(dr[columnIndex[taxCode]]),
						Phone = ToString(dr[columnIndex[phone]]),
						Fax = ToString(dr[columnIndex[fax]]),
						VietnamesAddress = ToString(dr[columnIndex[vnAddress]]),
						EnglishAddress = ToString(dr[columnIndex[enAddress]])
					};
					if (!string.IsNullOrWhiteSpace(company.VietnamesName) || 
							!string.IsNullOrWhiteSpace(company.EnglishName))
					{
						companies.Add(company);
					}
				}
			}

			return companies;
		}

		private Dictionary<string, int> GetColumnIndex(DataTable dtData, Dictionary<string, bool> columnNameHash,
			out int columnHeaderIndex)
		{
			columnHeaderIndex = 0;

			string key;
			Dictionary<string, int> columnIndexHash = new Dictionary<string, int>();
			foreach (DataRow dr in dtData.Rows)
			{
				for (int i = 0; i < dr.ItemArray.Length; i++)
				{
					key = ToString(dr[i]);
					if (columnNameHash.ContainsKey(key))
					{
						columnIndexHash.Add(key, i);
					}
				}

				columnHeaderIndex++;
				if (columnIndexHash.Count > 0)
				{
					break;
				}
			}

			return columnIndexHash;
		}

		private string ToString(object value)
		{
			return value != null ? value.ToString() : string.Empty;
		}
		
		#endregion

		#region Utility functions

		private void ResetGenerateCode()
		{
			GENERATE_CODE = 0;
		}

		private string RandomString(int length)
		{
			string rdString;
			while (true)
			{
				rdString = (++GENERATE_CODE).ToString().PadLeft(length, '0');
				if (!_companyHash.ContainsKey(rdString))
				{
					break;
				}
			}

			return rdString;
		}

		private string CopyFile(string path, bool isUpdate)
		{
			// 1. check logo company folder first
			if (!Directory.Exists(GlobalConst.COMPANY_LOGO_FOLDER))
			{
				Directory.CreateDirectory(GlobalConst.COMPANY_LOGO_FOLDER);
			}

			string fileNameFromPath = Path.GetFileName(path);
			string fileInComLogoFolder = Path.Combine(GlobalConst.COMPANY_LOGO_FOLDER, fileNameFromPath);
			if (File.Exists(fileInComLogoFolder))
			{
				if (isUpdate) //  UPDATE => do nothing
				{
					return fileInComLogoFolder;
				}

				// Action = ADD => need to pad extension string
				int dotIndex = fileNameFromPath.IndexOf('.');
				if (dotIndex == -1) // invalid file name (*_*)
				{
					return fileInComLogoFolder;
				}

				string fileWithoutExtension = fileNameFromPath.Substring(0, dotIndex);
				fileWithoutExtension += "_" + Guid.NewGuid().ToString(); // auto-include extension string

				string extension = fileNameFromPath.Substring(dotIndex + 1, fileNameFromPath.Length - dotIndex - 1);
				fileInComLogoFolder = Path.Combine(GlobalConst.COMPANY_LOGO_FOLDER,
					fileWithoutExtension + "." + extension);
			}

			try
			{
				_xmlCompany.Copy(path, fileInComLogoFolder, false);
			}
			catch (Exception)
			{
				throw;
			}

			return fileInComLogoFolder;
		}

		#endregion

		public bool CheckCompanyName(string vnComName, string enComName, string Id = null)
		{
			bool matched = false;

			Company[] companies = _companyHash.Values.ToArray();
			foreach (Company com in companies)
			{
				if (string.Equals(com.VietnamesName, vnComName, StringComparison.OrdinalIgnoreCase) ||
					string.Equals(com.EnglishName, enComName, StringComparison.OrdinalIgnoreCase))
				{
					matched = true;
					if (Id != null)
					{
						if (!string.Equals(com.ID, Id))
						{
							break;
						}

						matched = false;
					}
					else
					{
						break;
					}
				}
			}

			return matched;
		}

		/*public int Insert(List<Company> entities)
		{
			if (entities != null && entities.Count > 0)
			{
				string taxCode;
				foreach (Company company in entities)
				{
					taxCode = company.TaxCode;
					if (!IsExist(taxCode))
					{
						// 1. process logo file
						//if (!string.IsNullOrWhiteSpace(company.LogoPath))
						//{
						//    company.LogoPath = CopyFile(company.LogoPath, false);
						//}

						// 2. add to hash
						_companyHash.Add(taxCode, company);
					}
				}

				// 3. write to file
				Write2File();
				return 1;
			}

			return 0;
		}*/

		/*public int Delete(Company entity)
		{
			string taxCode = entity.TaxCode;
			if (!IsExist(taxCode))
			{
				return 0;
			}

			// 1. delete logo file
			//string logoPath = entity.LogoPath;
			//if (!string.IsNullOrWhiteSpace(logoPath))
			//{
			//    _xmlCompany.Delete(logoPath);
			//}

			// 2. delete in hash
			_companyHash.Remove(taxCode);

			// 3. write to file
			Write2File();
			return 1;
		}*/
	}
}
